/************mmap_ioremap.c**************/
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/errno.h>
#include <linux/mm.h>
#include <linux/wrapper.h> /* for mem_map_(un)reserve */
#include <asm/io.h> /* for virt_to_phys */
#include <linux/slab.h> /* for kmalloc and kfree */

MODULE_PARM(mem_start, "i");
MODULE_PARM(mem_size, "i");

static int mem_start = 101, mem_size = 10;
static char* reserve_virt_addr;
static int major;

int mmapdrv_open(struct inode* inode, struct file* file);
int mmapdrv_release(struct inode* inode, struct file* file);
int mmapdrv_mmap(struct file* file, struct vm_area_struct* vma);

static struct file_operations mmapdrv_fops =
{
 owner: THIS_MODULE, mmap : mmapdrv_mmap, open : mmapdrv_open, release :
 mmapdrv_release,
};

int init_module(void)
{
    if ((major = register_chrdev(0, "mmapdrv", &mmapdrv_fops)) < 0)
    {
        printk("mmapdrv: unable to register character device/n");
        return (-EIO);
    }
    printk("mmap device major = %d/n", major);

    printk("high memory physical address 0x%ldM/n", virt_to_phys(high_memory) /
        1024 / 1024);

    reserve_virt_addr = ioremap(mem_start * 1024 * 1024, mem_size * 1024 * 1024);
    printk("reserve_virt_addr = 0x%lx/n", (unsigned long)reserve_virt_addr);
    if (reserve_virt_addr)
    {
        int i;
        for (i = 0; i < mem_size * 1024 * 1024; i += 4)
        {
            reserve_virt_addr[i] = 'a';
            reserve_virt_addr[i + 1] = 'b';
            reserve_virt_addr[i + 2] = 'c';
            reserve_virt_addr[i + 3] = 'd';
        }
    }
    else
    {
        unregister_chrdev(major, "mmapdrv");
        return -ENODEV;
    }
    return 0;
}

/* remove the module */
void cleanup_module(void)
{
    if (reserve_virt_addr)
        iounmap(reserve_virt_addr);

    unregister_chrdev(major, "mmapdrv");
    return;
}

int mmapdrv_open(struct inode* inode, struct file* file)
{
    MOD_INC_USE_COUNT;
    return (0);
}

int mmapdrv_release(struct inode* inode, struct file* file)
{
    MOD_DEC_USE_COUNT;
    return (0);
}

int mmapdrv_mmap(struct file* file, struct vm_area_struct* vma)
{
    unsigned long offset = vma->vm_pgoff << PAGE_SHIFT;
    unsigned long size = vma->vm_end - vma->vm_start;

    if (size > mem_size * 1024 * 1024)
    {
        printk("size too big/n");
        return (-ENXIO);
    }

    offset = offset + mem_start * 1024 * 1024;

    　/* we do not want to have this area swapped out, lock it */
    vma->vm_flags |= VM_LOCKED;
    if (remap_page_range(vma, vma->vm_start, offset, size, PAGE_SHARED))
    {
        printk("remap page range failed/n");
        return -ENXIO;
    }
    return (0);
}